module FETCH(clk, reset, hold, mem_addr, icode_d, icode_e, icode_m, valM, CC, mem_data, icode, ifun, rA, rB, valC, valP, WAIT, lock);
input clk;
input reset;
input hold;
input [3:0] icode_d;
input [3:0] icode_e;
input [3:0] icode_m;
input [31:0] valM;
input [31:0] CC;
input [15:0] mem_data;

output [31:0] mem_addr;
output [3:0] icode;
output [3:0] ifun;
output [3:0] rA;
output [3:0] rB;
output [31:0] valC;
output [31:0] valP;
output WAIT;

wire [31:0] currentPC;
wire [31:0] newPC;
wire [31:0] pcIn;
wire [1:0] counter;

wire [47:0] data;
wire [47:0] data_reg;

wire hold_internal;
wire finished;
wire Bch;
wire [47:0] data_decode;
output lock;

assign data_decode = (icode_m == 9) ? 47'b0: data;
assign WAIT = hold | hold_internal | !finished;

/*
	Logica de valP para instrucoes de numero impar de bytes ou numero par de bytes desalinhado
	Se for impar, adiciona 1, se for par e desalinhado nao adiciona nada e adiciona 2 para todo o resto
*/

assign valP = finished?
				      ((icode == 0 || icode == 1 || icode == 7 || icode == 8 || icode == 9)? 
							currentPC + 1
							:((currentPC[0])? 
								currentPC: 
								currentPC + 2))
						: currentPC + 2;

LockSignal lck(.reset(reset), .icode(icode), .ifun(ifun), .finished(finished), .hold(hold | hold_internal), .clk(clk), .lock(lock));
						
Register DTA (
				.clk(clk), 
				.in(data), 
				.out(data_reg));

Register PC (
				.clk(clk), 
				.in(pcIn), 
				.out(currentPC));

BranchLogic bl(
				.icode(icode),
				.ifun(ifun), 
				.CC(CC), 
				.Bch(Bch));
				
PC_Incrementer incrementer(
				.pc_in(currentPC),
				.icode(icode),
				.valC(valC), 
				.Bch(Bch),
				.icode_m(icode_m),
				.valM(valM),
				.finished(finished),
				.pc_out(newPC));

Counter2b cntr(
				.clk(clk), 
				.reset(!finished),
				.hold(hold | hold_internal),
				.out(counter));

ReadMemory read(
				.address(currentPC), 
				.counter(counter),
				.data_in(data_reg),
				.mem_addr(mem_addr), 
				.mem_data(mem_data), 
				.data(data));
					 
FetchDataDecoder dec(
				.reset(reset),
				.data(data_decode),
				.icode(icode),
				.ifun(ifun),
				.rA(rA),
				.rB(rB),
				.valC(valC));

FetchFinishedGenerator fgen(
				.icode(icode), 
				.pc(currentPC), 
				.counter(counter), 
				.icode_d(icode_d), 
				.icode_e(icode_e), 
				.finished(finished), 
				.hold(hold_internal));					 

assign pcIn = (!reset)? 0: ((hold | hold_internal)? currentPC: newPC);
endmodule
